package com.zcw;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Advapi32Util;
import com.sun.jna.platform.win32.WinReg;
import com.sun.jna.win32.StdCallLibrary;
import com.zcw.entity.Image;
import com.zcw.util.CommUtils;
import com.zcw.util.PropertiesUtils;

public class Main {

	/**
	 * 下载几天的图片，大于等于1或-1，默认1<br>
	 * 1表示只下载今天图片，2表示下载今天与昨天的图片，3表示下载今天、昨天、前天的图片，以此类推。目前bing允许最多下载6天图片<br>
	 * -1时表示程序自动判断天数<br>
	 * 下载图片时会判断本地是否已经存在，若存在则不下载本日图片
	 */
	private static int downloadPicNumMax = -1;

	/** 本地原始图片保存路径，程序启动时初始化，可通过配置文件修改 */
	private static String downloadpath = null;

	/** 桌面背景图片是否添加文字说明（1：添加，0：不添加） */
	private static int picExplain = 1;

	/** 文字说明背景是否添加阴影（仅picExplain=1时有效） */
	private static int backExplainShadow = 1;

	/** 本地图片文件添加水印之后的文件名，此文件只有一个，每天新的图片修改完成之后覆盖昨天的文件 */
	private static String finalPicName = "!bingpic.jpg";

	/** 用户缓存本地已经存在的图片名称，用于判断图片是否已经存在，避免每次都要读取文件名称 */
	private static String existsFileNameStr = null;

	/**
	 * 延长图片说明的阴影部分到图片最底部。<br>
	 * 因为使用了系统美化工具，任务栏为透明，同时右下角时间显示颜色为白色，导致部分图片下时间显示不明显（ 与不添加阴影的图片说明类似）。
	 */
	private static final int TASK_HEIGHT = 50;

	static {
		// 初始化图片存储路径
		downloadpath = PropertiesUtils.getProperty("downloadpath");
		File path = new File(downloadpath);
		if (!path.exists()) {
			path.mkdirs();
		}
		downloadpath = path.getAbsolutePath() + File.separator;

		// 初始化图片下载天数
		downloadPicNumMax = CommUtils.objToInt(PropertiesUtils.getProperty("downloadPicNumMax"));
		if (downloadPicNumMax != -1 && downloadPicNumMax < 1) {
			downloadPicNumMax = 1;
		}

		// 桌面背景图片是否添加文字说明
		picExplain = CommUtils.objToInt(PropertiesUtils.getProperty("picExplain"));
		if (picExplain != 0 && picExplain != 1) {
			picExplain = 1;
		}

		// 桌面背景图片是否添加文字说明
		backExplainShadow = CommUtils.objToInt(PropertiesUtils.getProperty("backExplainShadow"));
		if (backExplainShadow != 0 && backExplainShadow != 1) {
			backExplainShadow = 1;
		}
		CommUtils.println();
		CommUtils.println();
		CommUtils.printf("系统初始化完成");
		CommUtils.printf("图片保存路径为【%s】", downloadpath);
		if (downloadPicNumMax == -1) {
			CommUtils.printf("下载图片数量【最大化】");
			downloadPicNumMax = Integer.MAX_VALUE;
		} else {
			CommUtils.printf("下载图片数量【%s】", downloadPicNumMax);
		}
		CommUtils.printf("是否添加文字说明【%s】", picExplain == 1 ? "是" : "否");
		if (picExplain == 1) {
			CommUtils.printf("文字说明是否添加阴影【%s】", backExplainShadow == 1 ? "是" : "否");
		}
	}

	/**
	 * @project com.zcw
	 * @Title: getPicUrl
	 * @Description: 通过访问bing API获取图片请求地址及说明
	 * @author zhangcunwei
	 * @createDate 2020年4月18日 下午12:23:50
	 */
	public static void main(String[] args) throws Exception {
		// 获取所有的图片信息
		CommUtils.println();
		List<Image> imagesInfo = getImages();
		if (imagesInfo == null || imagesInfo.size() == 0) {
			CommUtils.println("未获取图片下载信息，程序结束");
			return;
		}
		CommUtils.println();
		for (int i = 0; i < imagesInfo.size(); i++) {
			Image image = imagesInfo.get(i);
			String date = image.getEnddate();
			CommUtils.println();
			CommUtils.printf("开始处理【%s】的图片", date);

			// 当这天的照片不存在时，才下载
			if (isExists(date)) {
				CommUtils.printf("【%s】照片已存在，开始处理下一天", date);
				CommUtils.println();
				continue;
			}
			CommUtils.printf("【%s】照片不存在，开始下载", date);

			String picDownloadUrl = "https://cn.bing.com" + image.getUrl();
			String downloadPicFileName = downLoadFromUrl(date, picDownloadUrl);
			if (CommUtils.isEmpty(downloadPicFileName)) {
				CommUtils.printf("【%s】图片下载失败", date);
				continue;
			}
			CommUtils.printf("【%s】图片下载完成", date);
			CommUtils.println();

			// 如果是第一张照片，则添加水印，并设置为桌面背景图片
			if (i != 0) {
				TimeUnit.SECONDS.sleep(3);
				continue;
			}

			// 添加水印,添加水印时，会生成最终带有水印的文件finalPicName，当不添加水印时，默认当前下载的文件为最终文件名，准备设置为桌面背景
			if (picExplain == 1) {
				TimeUnit.SECONDS.sleep(1);
				CommUtils.println();
				CommUtils.printf("添加水印说明");
				addMarkContent(downloadPicFileName, image.getCopyright());
				CommUtils.printf("水印说明添加完成");
			} else {
				finalPicName = downloadPicFileName;
			}

			// 设置为桌面背景
			TimeUnit.SECONDS.sleep(1);
			CommUtils.println();
			CommUtils.printf("设置桌面背景");
			setImageToBackGroudPic(downloadpath + finalPicName);
			CommUtils.printf("设置桌面背景完成");
			TimeUnit.SECONDS.sleep(3);
		}
		CommUtils.println();
		CommUtils.println("程序运行结束");
	}

	/**
	 * @Title: getImages
	 * @author: zhangcunwei
	 * @date: 2021年8月9日 上午12:28:26
	 * @Description: 通过API获取图片下载信息
	 * @return List<Image>
	 */
	private static List<Image> getImages() {
		List<Image> allList = new ArrayList<Image>();
		try {
			for (int i = 0; i < Integer.MAX_VALUE; i++) {
				int idx = i * 7;
				String requestUrl = "https://cn.bing.com/HPImageArchive.aspx?format=js&idx=" + idx + "&n=10&nc=1498657985239&pid=hp&video=1";
				CommUtils.printf("请求地址为【%s】", requestUrl);
				byte[] httpData = httpRequest(requestUrl);
				String htmlJSON = new String(httpData, "UTF-8");
				CommUtils.printf("请求返回信息为【%s】", htmlJSON);

				JSONObject json = JSON.parseObject(htmlJSON);
				JSONArray jsonarray = json.getJSONArray("images");
				List<Image> list = JSON.parseArray(jsonarray.toJSONString(), Image.class);
				int addImage = 0;
				for (Image image : list) {
					if (allList.contains(image)) {
						continue;
					}
					allList.add(image);
					addImage++;
					if (allList.size() >= downloadPicNumMax) {
						break;
					}
				}
				if (addImage == 0) {
					CommUtils.printf("未获取新的图片信息，图片信息获取结束");
					CommUtils.println(allList);
					break;
				} else {
					CommUtils.printf("获取【%d】条图片信息", addImage);
					TimeUnit.SECONDS.sleep(1);
				}
				if (allList.size() >= downloadPicNumMax) {
					break;
				}
			}
		} catch (Exception e) {
			CommUtils.println("获取图片信息失败");
			e.printStackTrace();
		}

		return allList;
	}

	/**
	 * @Title: isExists
	 * @author: zhangcunwei
	 * @date: 2021年8月8日 下午10:44:50
	 * @Description: 判断某天的照片是否存在
	 * @param date
	 * @return boolean
	 */
	private static boolean isExists(String date) {
		if (CommUtils.isEmpty(existsFileNameStr)) {
			List<String> list = Arrays.asList(new File(downloadpath).list());
			existsFileNameStr = String.join(",", list);
		}
		return existsFileNameStr.indexOf(date) != -1;
	}

	/**
	 * @project com.zcw
	 * @Title: downLoadFromUrl
	 * @Description: 从picUrl下载图片，重命名为downloadPicName并保存到downloadpath路径下,返回图片名称，为添加水印做准备
	 * @author zhangcunwei
	 * @param picDownloadUrl
	 * @param picDownloadUrl
	 * @createDate 2020年4月18日 下午12:26:21
	 * @return
	 * @throws IOException
	 */
	public static String downLoadFromUrl(String date, String picDownloadUrl) throws IOException {
		Date nowTime = new Date();
		SimpleDateFormat time = new SimpleDateFormat("_HHmmss");
		String sysDate = time.format(nowTime);
		String downloadPicName = date + sysDate + ".jpg";
		try {
			CommUtils.printf("【%s】图片下载地址为【%s】", date, picDownloadUrl);
			// 获取自己数组
			byte[] httpData = httpRequest(picDownloadUrl);
			// 文件保存位置
			File path = new File(downloadpath);
			if (!path.exists()) {
				path.mkdirs();
			}

			File dir = new File(path, downloadPicName);
			if (!dir.exists())
				dir.createNewFile();
			FileOutputStream fos = new FileOutputStream(dir);
			fos.write(httpData);
			if (fos != null) {
				fos.close();
			}
			CommUtils.printf("下载图片大小为【%d】", dir.length());
			if (dir.length() == 0) {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return downloadPicName;
	}

	/**
	 * @Title: addMarkContent
	 * @author: zhangcunwei
	 * @date: 2021年8月9日 上午12:31:37
	 * @Description: 添加图片水印
	 * @param downloadPicFileName 需要添加水印的图片名称
	 * @param markContent         水印内容
	 * @throws IOException void
	 */
	private static void addMarkContent(String downloadPicFileName, String markContent) throws IOException {

		String srcImgPath = downloadpath + downloadPicFileName;
		String tempFilePath = downloadpath + "temp.jpg";
		String finalPic = downloadpath + finalPicName;

		// 读取源文件的宽度和高度
		BufferedImage srcImg = ImageIO.read(new File(srcImgPath));
		int imgWidth = srcImg.getWidth();
		int imgHeight = srcImg.getHeight();

		// 设置字体信息
		int fontSize = 26;
		Font font = new Font("微软雅黑", Font.PLAIN, fontSize);

		// 根据说明文字获取文字长度
		int textLength = getTextLength(markContent);

		// 根据文字长度和字体大小设置文字背景阴影区的大小
		int shadowWidth = textLength * fontSize;
		int shadowHeight = fontSize * 3 / 2;

		// 根据图片高度和宽度获得文字的开始位置
		int startX = imgWidth - shadowWidth;
		int startY = imgHeight - shadowHeight - TASK_HEIGHT;
		int strX = startX + fontSize / 2;
		int strY = startY + fontSize + fontSize / 6;

		// 文字背景阴影延长到图片底部，优化任务栏透明后系统时间显示效果
		shadowHeight += TASK_HEIGHT;

		// 添加背景阴影
		if (backExplainShadow == 1) {
			createDropShadow(srcImg, tempFilePath, startX, startY, shadowWidth, shadowHeight);
			CommUtils.printf("图片阴影添加成功");
		} else {
			BufferedImage shadow = new BufferedImage(srcImg.getWidth(), srcImg.getHeight(), BufferedImage.TYPE_INT_ARGB);
			Graphics2D g = shadow.createGraphics();
			g.drawImage(srcImg, 0, 0, null);
			g.dispose();
			ImageIO.write(shadow, "PNG", new File(tempFilePath));
		}

		// 添加图片说明
		createString(tempFilePath, markContent, finalPic, font, strX, strY);
		CommUtils.printf("图片说明添加成功");

		// 删除临时文件
		new File(tempFilePath).deleteOnExit();
	}

	/**
	 * @Title: setImageToBackGroudPic
	 * @author: zhangcunwei
	 * @date: 2021年8月9日 上午12:33:16
	 * @Description: 这只桌面背景图片
	 * @param img void
	 */
	public static void setImageToBackGroudPic(String img) {
		Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Control Panel\\Desktop", "Wallpaper", img);
		Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Control Panel\\Desktop", "WallpaperStyle", "10"); // fill
		Advapi32Util.registrySetStringValue(WinReg.HKEY_CURRENT_USER, "Control Panel\\Desktop", "TileWallpaper", "0"); // no
		int SPI_SETDESKWALLPAPER = 0x14;
		int SPIF_UPDATEINIFILE = 0x01;
		int SPIF_SENDWININICHANGE = 0x02;
		boolean result = MyUser32.INSTANCE.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, img, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
		CommUtils.println("设置桌面背景" + (result ? "成功" : "失败"));
	}

	private interface MyUser32 extends StdCallLibrary {
		MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class);

		boolean SystemParametersInfoA(int uiAction, int uiParam, String fnm, int fWinIni);
	}

	/**
	 * @Title: httpRequest
	 * @author: zhangcunwei
	 * @date: 2021年8月9日 上午12:34:15
	 * @Description: 请求API公共方法
	 * @param requestUrl
	 * @return
	 * @throws IOException byte[]
	 */
	private static byte[] httpRequest(String requestUrl) throws IOException {
		InputStream inputStream = null;
		ByteArrayOutputStream bos = null;
		try {
			URL url = new URL(requestUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			// 设置超时间为3秒
			conn.setConnectTimeout(3 * 1000);
			// 防止屏蔽程序抓取而返回403错误
			conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
			// 得到输入流
			inputStream = conn.getInputStream();
			// 获取自己数组
			byte[] buffer = new byte[1024];
			int len = 0;
			bos = new ByteArrayOutputStream();
			while ((len = inputStream.read(buffer)) != -1) {
				bos.write(buffer, 0, len);
			}
			bos.close();
			return bos.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				inputStream.close();
			}
			if (bos != null) {
				bos.close();
			}
		}
		return null;
	}

	/**
	 * @Title: createDropShadow
	 * @author: zhangcunwei
	 * @date: 2021年8月9日 上午12:34:49
	 * @Description: 添加背景阴影
	 * @param image
	 * @param descFilePath
	 * @param startX
	 * @param startY
	 * @param shadowWidth
	 * @param shadowHeight
	 * @throws IOException void
	 */
	private static void createDropShadow(BufferedImage image, String descFilePath, int startX, int startY, int shadowWidth, int shadowHeight)
			throws IOException {
		BufferedImage shadow = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = shadow.createGraphics();
		g.drawImage(image, 0, 0, null);
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.6f));
		g.setColor(Color.gray);
		g.fillRoundRect(startX, startY, shadowWidth, shadowHeight, 50, 50);
		g.dispose();
		ImageIO.write(shadow, "PNG", new File(descFilePath));
	}

	/**
	 * @Title: createString
	 * @author: zhangcunwei
	 * @date: 2021年8月9日 上午12:35:07
	 * @Description: 添加图片说明
	 * @param srcImgPath
	 * @param waterMarkContent
	 * @param descFilePath
	 * @param font
	 * @param strX
	 * @param strY
	 * @return
	 * @throws IOException RenderedImage
	 */
	private static RenderedImage createString(String srcImgPath, String waterMarkContent, String descFilePath, Font font, int strX, int strY)
			throws IOException {
		// 读取源文件
		BufferedImage image = ImageIO.read(new File(srcImgPath));
		BufferedImage shadow = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = shadow.createGraphics();
		g.drawImage(image, 0, 0, null);
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f));
		g.setColor(Color.white);
		g.setFont(font);
		g.drawString(waterMarkContent, strX, strY);
		g.dispose();
		ImageIO.write(shadow, "PNG", new File(descFilePath));
		return shadow;
	}

	/**
	 * 获取文本长度。汉字为1:1，英文和数字为2:1
	 */
	private static int getTextLength(String text) {
		int length = text.length();
		for (int i = 0; i < text.length(); i++) {
			String s = String.valueOf(text.charAt(i));
			if (s.getBytes().length > 1) {
				length++;
			}
		}
		length = length % 2 == 0 ? length / 2 : length / 2 + 1;
		return length;
	}

}
